use core::ptr::copy_nonoverlapping;
use ufmt::uWrite;

pub trait LogWriter {
    
    fn wr<'a>(&'a self) -> MutByteRef<'a>;
    fn flush<'a>(&self, bytes: MutByteRef<'a>);
}

pub static mut GLOBAL_LOG_WRITER: &'static dyn LogWriter = &NOP;

pub unsafe fn set_log_writer(w: &'static dyn LogWriter) {
    GLOBAL_LOG_WRITER = w;
}

#[macro_export]
macro_rules! log {
    ($($args: expr),*) => {
        {
            use $crate::ufmt;
            let mut bw = unsafe { $crate::log::GLOBAL_LOG_WRITER.wr() };
            _ = $crate::ufmt::uwrite!(bw, "{}:{}=>", file!(), line!());
            _ = $crate::ufmt::uwrite!(bw, $($args),*);
            unsafe { $crate::log::GLOBAL_LOG_WRITER.flush(bw); }
        }
    };
}

pub struct NOP;

impl LogWriter for NOP {
    fn flush<'a>(&self, _bytes: MutByteRef<'a>) {
    }

    fn wr<'a>(&'a self) -> MutByteRef<'a> {
        MutByteRef::new(&mut [])
    }
}

pub struct MutByteRef<'a> {
    buf: &'a mut [u8],
    index: usize,
}

impl<'a> MutByteRef<'a> {

    pub fn new(buf: &'a mut [u8]) -> Self {
        Self { buf, index: 0 }
    }

    #[inline(always)]
    pub fn data(&'a self) -> &'a [u8] {
        &self.buf[..self.index]
    }
}

impl<'a> uWrite for MutByteRef<'a> {
    type Error = WriteError;
    fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
        let len = s.len();
        if (self.index + len) > self.buf.len() {
            return Err(WriteError::OutofRange);
        }
        unsafe {
            copy_nonoverlapping(
                s.as_ptr(),
                self.buf.as_mut_ptr().add(self.index),
                len
            );
            self.index += len;
        }
        Ok(())
    }
}

pub enum WriteError {
    OutofRange,
}










